#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "clstub.h"
#include "clsv.h"
#include "hton.h"
#include "ntoh.h"
#include "sockcomADT.h"
#include "error.h"
#include "tickets.h"

#define FILE_BUFFER		MAX_DATA
#define	LIST_BUFFER		MAX_LIST

extern connInfoT connInfo;
extern userInfoT userInfo;

static CLSV_BUFFER clsv_buff;		/* communications buffer */

/* start communications session */
int
start_session(commType com)
{
    if ((comm = initComm(com, connInfo.ip, connInfo.port)) == NULL)
		return 0;
    return 1;
}

/* end communications session */
int
end_session(void)
{
    if (comm != NULL)
        closeComm(comm, CLIENT);
    
    return 1;
}

static int
send_rcv(CLSV_BUFFER * p, OPC opcode, int service)
{
	int bytes;
	void * stream;
	
	/* allocate memory for transport stream */
	if ((stream = malloc(sizeof(CLSV_BUFFER))) == NULL)
	{
		fprintf(stderr, "Memory allocation error\n");
		return -1;
	}
	
	/* convert struct to stream and send it to the server */
	bytes = hton_clsv_buffer(p, &stream, opcode, CL);
	send_packet(comm, stream, bytes, service);
	
	/* receive reply from server and convert stream to corresponding struct */
	if ((bytes = receive_packet(comm, stream, sizeof(CLSV_BUFFER),
								service)) == 0) 
		return 0;
	ntoh_clsv_buffer(&p, stream, SV);
	
	/* free stream and return */
	free(stream);
	if (p->opcode == opcode)
		//return qty - sizeof(OPC);
		return 1;
	else
		return -1;
}

static int
send_only(CLSV_BUFFER * p, OPC opcode, int service)
{
	int bytes;
	void * stream;
	
	/* allocate memory for transport stream */
	if ((stream = malloc(sizeof(CLSV_BUFFER))) == NULL)
	{
		fprintf(stderr, "Memory allocation error\n");
		return -1;
	}
	
	/* convert struct to stream and send it to the server */
	bytes = hton_clsv_buffer(p, &stream, opcode, CL);
	send_packet(comm, stream, bytes, service);
	
	free(stream);
	
	return bytes;
}

static int
recv_only(CLSV_BUFFER * p, OPC opcode, int service)
{
	int bytes;
	void * stream;
	STAT status;
	
	/* allocate memory for transport stream */
	if ((stream = malloc(sizeof(CLSV_BUFFER))) == NULL)
	{
		fprintf(stderr, "Memory allocation error\n");
		return -1;
	}
	
	/* receive reply from server and convert stream to corresponding struct */
	if ((bytes = receive_packet(comm, stream, sizeof(CLSV_BUFFER),
								service)) == 0) 
		return 0;
	
	//printf("antes del memcpy\n");
	/* copy status so it can be returned */
	memcpy((void *)&status, (void *)((char *)stream+sizeof(OPC)),
					sizeof(STAT));
	status = ntohs(status);
	//printf("desp del memcpy\n");
	
	//printf("antes del noth\n");
	ntoh_clsv_buffer(&p, stream, SV);
	//printf("desp del noth\n");
	
	/* free stream and return */
	free(stream);
	//printf("despues del free(stream)\n");
	
	return status;
}


STAT
passwd(char * user, char * oldpass, char * newpass, int service)
{
	CLSV_PASSWD * pc;
	SVCL_PASSWD * ps;
	
	pc = &clsv_buff.data.clsv_passwd;	
	ps = &clsv_buff.data.svcl_passwd;
	
	printf("service = %d\n", service);
	strcpy(pc->user, user);
	memcpy(pc->oldpass, oldpass, MAX_EPASS);
	int i;
	printf("oldpass:");
	for (i=0; i<32; i++)
		printf("%d-", (int)(pc->oldpass)[i]);
	printf("\n");
	memcpy(pc->newpass, newpass, MAX_EPASS);
	printf("newpass:");
	for (i=0; i<32; i++)
		printf("%d-", (int)(pc->oldpass)[i]);
	printf("\n");
	
	
	switch (send_rcv(&clsv_buff, PASSWD, service))
	{
		case 0:
			fprintf(stderr, "Bytes read different than expected\n");
			return -1;
		case -1:
			fprintf(stderr, "Operation code conflict\n");
			return -1;
		default:
			return ps->status;
	}
}

/* get ticket */
STAT
get_ticket(char * filename, userInfoT userInfo, ticketP * tickp)
{
	STAT status;
	CLSV_GET_TICKET * pc;
	SVCL_GET_TICKET * ps;
	
	pc = &clsv_buff.data.clsv_get_ticket;
	ps = &clsv_buff.data.svcl_get_ticket;
	
	strcpy(pc->user, userInfo.user);
	memcpy(pc->pass, userInfo.epass, MAX_EPASS);
	memcpy(pc->filename, filename, MAX_FILENAME);
	
	switch (send_rcv(&clsv_buff, GET_TICKET, _TCP_))
	{
		default:
			printf("status = %d\n", ps->status);
			if ((status = ps->status) >= 0)
			{
				tickp->dtime = ps->dtime;
				tickp->etime = ps->etime;
				tickp->ftime = ps->ftime;
				memcpy(tickp->id, ps->id, MAX_ETICKET);
			}
			return status;
	}
}

STAT
use_ticket(ticketT id, userInfoT userInfo, FILE * f)
{
	STAT status;
	CLSV_USE_TICKET * pc;
	COMM_DATA_BLOCK * ps;
	char buffer[FILE_BUFFER];
	
	pc = &clsv_buff.data.clsv_use_ticket;
	ps = &clsv_buff.data.comm_data_block;
	
	strcpy(pc->user, userInfo.user);
	memcpy(pc->pass, userInfo.epass, MAX_EPASS);
	memcpy(pc->ticket_id, id, MAX_EPASS);
	
	/* send packet requesting a ticket cash */
	send_only(&clsv_buff, USE_TICKET, _TCP_);
	
	int tot = 0;
	/* receive reply, and if everything is OK then download */
	while ((status = recv_only(&clsv_buff, DATA_BLOCK, _TCP_)) > 0)
	{
		tot += status;
		//printf("copied %d bytes to file\n", status);
		memcpy(buffer, ps->data, status);
		fwrite(buffer, 1, status, f);
	}
	
	printf("transferred %d bytes\n", tot);
	return status;
}

STAT
upload(char * filename, int size, char * category, FILE * f)
{
	STAT status;
	CLSV_UPLOAD * pc;
	SVCL_UPLOAD * ps;
	COMM_DATA_BLOCK * db;
	COMM_STATUS * st;
	char buffer[FILE_BUFFER];
	
	pc = &clsv_buff.data.clsv_upload;
	ps = &clsv_buff.data.svcl_upload;
	db = &clsv_buff.data.comm_data_block;
	st = &clsv_buff.data.comm_status;
	
	strcpy(pc->filename, filename);
	pc->size = size;
	strcpy(pc->category, category);
	
	/* send upload request and wait for server approval */
	switch (send_rcv(&clsv_buff, UPLOAD, _TCP_))
	{
		case 0:
			fprintf(stderr, "Bytes read different than expected\n");
			return -ERROR_BYTES_DIFF;
		case -1:
			fprintf(stderr, "Operation code conflict\n");
			return -ERROR_OPCODE_CONF;
		default:
			if ((status = ps->status) < 0)
				return status;
	}
	
	/* send file */
	while (status)
	{
		status = fread(buffer, 1, sizeof(buffer), f);
		db->status = status;
		memcpy(db->data, buffer, status);
		send_only(&clsv_buff, DATA_BLOCK, _TCP_);
	}
	
	/* receive upload acknowledge (or error, if any) */
	status = recv_only(&clsv_buff, STATUS, _TCP_);
	
	return status;
}

STAT
list_categories(void)
{
	int i;
	STAT status;
	COMM_LIST_BLOCK * ps;
	char buffer[MAX_LIST];
	
	ps = &clsv_buff.data.comm_list_block;
	
	/* send list request */
	send_only(&clsv_buff, LIST_CATEGORIES, _TCP_);
	
	/* receive and print */
	while ((status = recv_only(&clsv_buff, LIST_CATEGORIES, _TCP_)) > 0)
	{
		memcpy(buffer, ps->data, status);
		for (i=0; i<status; i++)
			putchar(buffer[i]);
	}
	
	return status;
}

STAT
list_users(userInfoT userInfo)
{
	STAT status;
	CLSV_LIST_USERS * pc;
	COMM_LIST_BLOCK * ps;
	int i;
	char buffer[LIST_BUFFER];
	
	pc = &clsv_buff.data.clsv_list_users;
	ps = &clsv_buff.data.comm_list_block;
	
	strcpy(pc->user, userInfo.user);
	memcpy(pc->pass, userInfo.epass, MAX_EPASS);
	
	/* send list request */
	send_only(&clsv_buff, LIST_USERS, _TCP_);
	
	/* receive and print */
	while ((status = recv_only(&clsv_buff, LIST_USERS, _TCP_)) > 0)
	{
		memcpy(buffer, ps->data, status);
		for (i=0; i<status; i++)
			putchar(buffer[i]);
	}
	
	return status;
}

STAT
list_files(userInfoT userInfo, char * category)
{
	STAT status;
	CLSV_LIST_FILES * pc;
	COMM_LIST_BLOCK * ps;
	int i;
	char buffer[LIST_BUFFER];
	
	pc = &clsv_buff.data.clsv_list_files;
	ps = &clsv_buff.data.comm_list_block;
	
	strcpy(pc->user, userInfo.user);
	memcpy(pc->pass, userInfo.epass, MAX_EPASS);
	strcpy(pc->category, category);
	
	/* send list request */
	send_only(&clsv_buff, LIST_FILES, _TCP_);
	
	/* receive and print */
	while ((status = recv_only(&clsv_buff, LIST_BLOCK, _TCP_)) > 0)
	{
		memcpy(buffer, ps->data, status);
		for (i=0; i<status; i++)
			putchar(buffer[i]);
	}
	
	return status;
}
